# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import datetime
import json

import webapp2
from google.appengine.ext import ndb


DEFAULT_NAMESPACE = 'default'
DEFAULT_JOBS_TO_SERVE = 20


class Job(ndb.Model):
  """Represents a single build job description.

  Attributes:
    description: A JSON blob representing the job itself.
    created: A timestamp for when this job was posted.
    last_served: A timestamp for the last time this job was served, usually
        to a polling buildbot. Default is epoch 0, so new jobs are "old".
    taken: A boolean signalling that this job has been successfully picked
        up by a poller and can be dropped.
  """
  description = ndb.JsonProperty()
  last_served = ndb.DateTimeProperty(
      default=datetime.datetime.utcfromtimestamp(0))
  taken = ndb.BooleanProperty(default=False)


class MainHandler(webapp2.RequestHandler):

  def get(self):
    self.response.write("""
<html>
  <body>
    <form action="/default/push" method="post">
      <div><textarea name="job" rows="3" cols="60"></textarea></div>
          <div><input type="submit" value="Add Job"></div>
    </form>
    <form action="/default/accept" method="post">
      <div><textarea name="job" rows="1" cols="60"></textarea></div>
          <div><input type="submit" value="Accept Job"></div>
    </form>
  </body>
</html>
""")


class PushHandler(webapp2.RequestHandler):

  def post(self, project):
    job = Job(description=self.request.get('job'), namespace=project)
    job.put()


class PullHandler(webapp2.RequestHandler):

  def post(self, project):
    # Get the jobs we'd like to serve.
    time_threshold = datetime.datetime.utcnow() - datetime.timedelta(seconds=30)
    query = Job.query(namespace=project)
    query = query.filter(Job.last_served < time_threshold)
    query = query.filter(Job.taken == False)
    query = query.order(Job.last_served)
    jobs = query.fetch(DEFAULT_JOBS_TO_SERVE)

    # Mark them as served.
    for job in jobs:
      job.last_served = datetime.datetime.utcnow()
      job.put()

    # Serve them.
    result = []
    for job in jobs:
      job_blob = json.loads(job.description)
      job_blob.update({'job_key': job.key.urlsafe()})
      result.append(job_blob)
    self.response.headers['Content-Type'] = 'application/json'
    self.response.write(json.dumps(result))


class PeekHandler(webapp2.RequestHandler):

  def get(self, project, job):
    # Get the jobs we'd like to serve.
    if job:
      job_key = ndb.Key(urlsafe=job, namespace=project)
      job = job_key.get()
      jobs = [job]
    else:
      time_threshold = (datetime.datetime.utcnow() -
                        datetime.timedelta(seconds=30))
      query = Job.query(namespace=project)
      query = query.filter(Job.last_served < time_threshold)
      query = query.filter(Job.taken == False)
      query = query.order(Job.last_served)
      jobs = query.fetch(DEFAULT_JOBS_TO_SERVE)

    # Serve them.
    result = []
    for job in jobs:
      job_blob = json.loads(job.description)
      job_blob.update({'job_key': job.key.urlsafe()})
      result.append(job_blob)
    self.response.headers['Content-Type'] = 'application/json'
    self.response.write(json.dumps(result))


class AcceptHandler(webapp2.RequestHandler):

  def post(self, project, job):
    job_key = ndb.Key(urlsafe=job, namespace=project)
    job = job_key.get()
    if job.taken:
      # This job has been previously accepted by someone else.
      self.response.set_status(409)
    job.taken = True
    job.put()


app = webapp2.WSGIApplication([
    ('/', MainHandler),
    ('/(.*)/push', PushHandler),
    ('/(.*)/pull', PullHandler),
    ('/(.*)/peek/?(.*)', PeekHandler),
    ('/(.*)/accept/(.*)', AcceptHandler),
])
